package structure

import (
	"context"
	"sync"
)

const (
	ControlStatus_Uninitialized uint8 = 0 // 未初始化 未启动 未关闭
	ControlStatus_Initialized   uint8 = 1 // 已初始化 未启动 未关闭
	ControlStatus_Opening       uint8 = 3 // 已初始化 已启动 未关闭
	ControlStatus_Closed        uint8 = 7 // 已初始化 已启动 已关闭
)

// 控制结构
// 提供 Init() Start() Stop() 的唯一调用限制,
// 底层使用sync.Once实现, 传参闭包中不能递归调用这三个函数, 否则将导致死锁
type Control struct {
	flag      uint8 // 111 close | start | init
	initOnce  sync.Once
	startOnce sync.Once
	stopOnce  sync.Once
	wg        sync.WaitGroup

	mainCtx    context.Context    // 自身及其派生结构状态控制
	mainCancel context.CancelFunc // 自身及其派生结构状态控制
	subCtx     context.Context    // 自身状态控制
	subCancel  context.CancelFunc // 自身状态控制
}

// 初始化控制参数 先初始化Context 再调用闭包
func (ctl *Control) Init(parent context.Context, f func()) {
	ctl.initOnce.Do(func() {
		ctl.mainCtx, ctl.mainCancel = context.WithCancel(parent)
		ctl.subCtx, ctl.subCancel = context.WithCancel(ctl.mainCtx)
		ctl.flag |= 1
		if f != nil {
			f()
		}
	})
}

// 获取控制器Context
func (ctl *Control) Context() context.Context {
	return ctl.mainCtx
}

// 控制计数器增加指定值
func (ctl *Control) Add(delta int) {
	ctl.wg.Add(delta)
}

// 控制器计数器减一
func (ctl *Control) Done() {
	ctl.wg.Done()
}

// 等待控制器计数器归零
func (ctl *Control) Wait() {
	ctl.wg.Wait()
}

// 检查控制器是否已初始化
func (ctl *Control) Initialized() bool {
	return ctl.flag > ControlStatus_Uninitialized
}

// 检查控制器是否在可用阶段
func (ctl *Control) Opening() bool {
	return ctl.flag == ControlStatus_Opening
}

// 检查控制器是否已关闭 未初始化也视作已关闭
func (ctl *Control) Closed() bool {
	return ctl.flag == ControlStatus_Uninitialized || ctl.flag == ControlStatus_Closed
}

// 控制器状态
func (ctl *Control) Status() uint8 {
	return ctl.flag
}

// 启动控制器
// 只能执行一次,后续调用将不会有任何动作
// 初始化之前调用将触发panic
func (ctl *Control) Start(f func()) {
	if !ctl.Initialized() {
		panic("call Start() before Control struct initialize")
	}

	ctl.startOnce.Do(func() {
		if ctl.Closed() {
			return
		}

		ctl.flag |= 2

		if f != nil {
			f()
		}
	})
}

// 停止控制器
// 只能执行一次,后续调用将不会有任何动作
// 初始化之前调用将触发panic
func (ctl *Control) Stop(f func()) {
	if !ctl.Initialized() {
		panic("call Stop() before Control struct initialize")
	}

	ctl.stopOnce.Do(func() {
		// 先关闭subCtx, 确保mainCtx及其派生Ctx能正常工作
		// 闭包执行完毕之后再将mainCtx及其派生Ctx状态置为关闭
		ctl.subCancel()

		ctl.flag |= 4
		if f != nil {
			f()
		}

		ctl.mainCancel()
	})
}

// 跨协程监听控制器关闭
func (ctl *Control) CtxDone() <-chan struct{} {
	return ctl.subCtx.Done()
}
